/*
*Container.fx
*
*Created on Aug 7, 2011, 19:07:58 PM
*/

package derbyclnt;

import derbyclnt.Config;
import derbyclnt.messages.*;
import derbyclnt.Logger.*;

import java.lang.Math;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import java.util.Random;

public class Engine extends CustomNode, derbyclnt.inter.IEngine {
    
    public var mainManager:derbyclnt.MainManager;
    
    //// CAR CONTROLS ////
    public var turnLeft:Boolean = false;
    public var turnRight:Boolean = false;
    public var accelerate:Boolean = false;
    public var applyBreak:Boolean = false;
    
    public var car: Car = Car {
        posX: Config.SCREEN_WIDTH/2
        posY: Config.SCREEN_HEIGHT/2 
    }
    
    public var otherCars: Car[] = [];
    
    public var gameBoard: Group = Group {
        content: [
        	car,
        	Group {
        	    content: bind otherCars
        	}
        ]
        
        focusTraversable: true;
        onKeyPressed: function(e:KeyEvent):Void {
            if (e.code == KeyCode.VK_ENTER) {
                if (not gameLoop.running or gameLoop.paused) {
                    startGame();
                }
            }
            if (e.code == KeyCode.VK_LEFT) {
                turnLeft = true;
            }
            if (e.code == KeyCode.VK_RIGHT) {
                turnRight = true;
            }
            if(e.code == KeyCode.VK_UP){
                car.moveAngle = car.faceAngle;
                accelerate = true;
            }
            if(e.code == KeyCode.VK_DOWN){
                car.moveAngle = car.faceAngle;
                applyBreak = true;
            }
        }
        onKeyReleased: function(e:KeyEvent):Void {
            if (e.code == KeyCode.VK_LEFT) {
                turnLeft = false;
            }
            if (e.code == KeyCode.VK_RIGHT) {
                turnRight = false;
            }
            if(e.code == KeyCode.VK_UP){
                accelerate = false;
            }
            if(e.code == KeyCode.VK_DOWN){
                applyBreak = false;
            }
        }
    }
    
    public function init (mgr:derbyclnt.MainManager):Void {
        mainManager = mgr;
        mainManager.setCar(car);
        mainManager.setOtherCars(otherCars);
        mainManager.setEngine(this);
    }
    
    override public function create(): Node {
        return gameBoard;
    }
    
    def gameLoop:Timeline = Timeline {
        repeatCount : Timeline.INDEFINITE
        keyFrames: [
        	KeyFrame {
        	    time: Config.REFRESH_RATE
        	    action: function() {
        	        updateGame();
        	    }
        	}
        ]
    }
    
    public function startGame(): Void {
        gameLoop.play();
    }
    
    public function updateGame():Void {
        driveCar();
        updateCars();
        syncWithServer();
    }
    
    function updateCars() {
        updateCar(car);
        for (c in otherCars){
            logd("update other car: id={c.clientId}, posX={c.posX}, posY={c.posY}");
            updateCar(c);
        }
    }
    
    function driveCar():Void {
        if (turnLeft) {
            car.faceAngle -= Config.CAR_ROTATION_VELOCITY;
        }
        if (turnRight) {
            car.faceAngle += Config.CAR_ROTATION_VELOCITY;
        }
        
        if(accelerate and car.velocity < 10){
            car.moveAngle = car.faceAngle;
            car.velocity += 1;
        }else{
            if(car.velocity > 0){
                car.velocity -= 0.1;
            } else if (car.velocity < -0.1) {
                car.velocity += 0.1
            }
        }
        if(applyBreak and car.velocity > -3){
            car.velocity -=1;
        }
    }
    
    function updateCar(car:Car):Void {
        
        var speedX = Math.sin(Math.toRadians(car.faceAngle)) * car.velocity;
        var speedY = -Math .cos(Math.toRadians(car.faceAngle)) * car.velocity;
        var newX = speedX + car.posX;
        var newY = speedY + car.posY;
        car.posX = newX;
        car.posY = newY;

        
        //handle wrapping for when car goes out of bounds...
        if(car.posX < 0 - car.width/2 ) {
            //car has moved off the left side,
            //so make it wrap to the right side...
            car.posX = Config.SCREEN_WIDTH;
        }
        if(car.posX > Config.SCREEN_WIDTH + car.width/2) {
            //car has moved off the right side,
            //so make it wrap to the left side...
            car.posX = 0;
        }
        if(car.posY < 0 - car.height/2){
            //car has moved off the bottom of the scene,
            //so make it wrap to the top...
            car.posY = Config.SCREEN_HEIGHT;
        }
        if(car.posY > Config.SCREEN_HEIGHT + car.height/2 )
        {
            //car has moved off the top of the scene,
            //so make it wrap to the bottom...
            car.posY = 0;
        }
    }
    
    function syncWithServer():Void {
        sendDataToServer();
        receiveDataFromServer();
    }
    
    function sendDataToServer():Void {
        if(car.clientId == -1) 
        	return;
         //send the car state 
        try {
            logd("The info about the car is sending to server");
            var carBuilder:CarProtos.Car.Builder = CarProtos.Car.newBuilder();
            
            carBuilder.setClientId(car.clientId);
            carBuilder.setMoveAngle(car.moveAngle);
            carBuilder.setVelocity(car.velocity);
            carBuilder.setPosX(car.posX); //TODO pass relative coordinates and sizes
            carBuilder.setPosY(car.posY);
            carBuilder.setFaceAngle(car.faceAngle);
            
            mainManager.getSocketManager().send(carBuilder.build(), MessageTypesProtos.MessageTypes.MESSAGE_CAR);
        } catch (e:java.lang.Exception) {
            loge(e.getMessage());
        }
    }
    
    function receiveDataFromServer():Void {
        mainManager.getSocketManager().receiveAll(mainManager);
    }
    
    /// INTERAPOBILITY ///
    
    override public function getCar():Car {
        return car;
    }
	override public function getOtherCars():Car[] {
	    return otherCars;
	}
	override public function getOtherCar(clientId:Integer):Car {
	    var foundCar:Car = null;
	    for (c in otherCars)
	    {
	        if (c.clientId == clientId)
	        	foundCar = c;
	    }
	    return foundCar;
	}
	override public function addOtherCar(clientId_:Integer):Car {
	    var newCar:Car = Car{ clientId:clientId_};
	    insert newCar into otherCars; 
	    return newCar;
	}
    

};
